// SPIN.CC
// Spin class for a text edit control, with up/down arrows.
// Jason Hood.
// 25 July to 3 August, 1996.
// Modified: 1 to 9 December.
//           2 February, 1997
//             Added ShiftCtrlAdjust for multiply/divide.
//           16 February
//             Used parent->AddAccelerator instead of just AddAccelerator

#include "spin.h"

DEFINE_RESPONSE_TABLE(TriButton, Window)
  E_LBUTTONDOWNT
  E_LBUTTONUP
  E_MOUSEMOVE
  E_MOVEOUTSIDE
  E_TIMER
END_RESPONSE_TABLE


void TriButton::PaintWindow(int x1, int y1, int x2, int y2) {

  BasicButton::PaintWindow(x1, y1, x2, y2);

  ViewBuffer *buf = GetSubBuffer(x1, y1, x2, y2);
  int x[3], y[3];

  x[0] = (cx1+cx2)/2;
  x[1] = cx1;
  x[2] = cx2;

  switch (butID) {
    case ID_UPTRI: y[0] = cy1;
                   y[1] = y[2] = cy2-1;
                   break;
    case ID_DNTRI: y[0] = cy2-1;
                   y[1] = y[2] = cy1+1;
                   x[1] +=1; x[2] -= 1;
                   break;
  }
  Polygon(buf, 3, x, y, 20);
  DeleteBuffer(buf);
}

void TriButton::LButtonDown(int x, int y, int key_stat, int timer) {
  BasicButton::LButtonDown(x, y, key_stat, timer);
  ws.QueueEvent(parent, event(W_NOTIFY, butID, 1011, key_stat));
  kbstat = key_stat;
  ws.StartTimer(this, 200);
  timing = true;
}

void TriButton::LButtonUp(int x, int y, int key_stat) {
  BasicButton::LButtonUp(x, y, key_stat);
  ws.StopTimer(this);
  timing = false;
}

void TriButton::MouseMove(int x,int y,int key_stat,int but_stat) {
  BasicButton::MouseMove(x, y, key_stat, but_stat);
  kbstat = key_stat;
  if (but_stat & MBS_LEFT_BUTTON && !timing) {
    timing = true;
    ws.StartTimer(this, 200);
  }
}

void TriButton::MoveOutside(int x,int y,int key_stat,int but_stat) {
  BasicButton::MoveOutside(x, y, key_stat, but_stat);
  timing = false;
  ws.StopTimer(this);
}

void TriButton::Timer() {
  ws.QueueEvent(parent, event(W_NOTIFY, butID, 1011, kbstat));
}


DEFINE_RESPONSE_TABLE(Spin, Window)
  E_BUTTONDOWNK(ID_UPTRI, cmUp)
  E_BUTTONDOWNK(ID_DNTRI, cmDown)
  E_KEYCODE(0xe0, 0x48, cmUp)           // Up normal/shift
  E_KEYCODE(0xe0, 0x8d, cmUp)           // Up control
  E_KEYCODE(0xe0, 0x50, cmDown)         // Down normal/shift
  E_KEYCODE(0xe0, 0x91, cmDown)         // Down control
END_RESPONSE_TABLE


Spin::Spin(Window* parent, ControlID ID, const char* title, int n,
                 int x, int y, int l) :
  Window(parent, NULL, x, y, x+l-1, y+SysFont->height*2+6,
         WA_VISABLE | WA_SAVEAREA | WA_TABSTOP),
  Min(0), Max(1),
  Adjust(1), ShiftAdjust(10), CtrlAdjust(100), ShiftCtrlAdjust(2),
  Edit(this, ID_EDIT, title, n, 0, 0, l-TriBtnW-2, EDIT_TYPE_NEG_NUM),
  Up(this, ID_UPTRI, l-TriBtnW, SysFont->height+1, TriBtnH),
  Down(this, ID_DNTRI, l-TriBtnW, SysFont->height+1+TriBtnH, TriBtnH),
  spinID(ID)
{
  // Calculate the absolute minimum and maximum values. eg. if n is 4 that
  // allows three characters - "-99" to "999".
  for (int j = n-2; j > 0; j--) Max *= 10;      // Saves including math.h
  absMin = -Max+1;                              // -(10^(n-2)) + 1
  absMax = Max = Max*10-1;                      // 10^(n-1) - 1
  Edit.RemoveAccelerators(&Edit);
  char *acc = strchr(title, '&');
  if (acc) parent->AddAccelerator(*(acc+1), this);
}

void Spin::SetNum(int num) {
  if (GetNum() == (num = Valid(num))) return;
  char temp[40];
  Edit.SetText(itoa(num, temp, 10));
  Edit.Paint(); Edit.RefreshWindow();
  ws.QueueEvent(parent, event(W_NOTIFY, spinID, 1012));
}


int  Spin::TestKeys(int kbstat) {
  if (kbstat & KBS_SHIFT) return ShiftAdjust;
  if (kbstat & KBS_CTRL) return CtrlAdjust;
  return Adjust;
}

void Spin::cmUp(int kbstat) {
  int num = GetNum();
  if ((kbstat & KBS_SHIFT) && (kbstat & KBS_CTRL)) num *= ShiftCtrlAdjust;
  else num += TestKeys(kbstat);
  SetNum(num);
}

void Spin::cmDown(int kbstat) {
  int num = GetNum();
  if ((kbstat & KBS_SHIFT) && (kbstat & KBS_CTRL)) num /= ShiftCtrlAdjust;
  else num -= TestKeys(kbstat);
  SetNum(num);
}
